﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.IO;
using System.Text.RegularExpressions;
using System.Threading;
using ICSharpCode.SharpZipLib.GZip;
using System.Windows.Forms;

namespace BigFileSearcher
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        private SaveFileDialog saveFileDialog;
        private OpenFileDialog openFileDialog;
        public MainWindow()
        {
            InitializeComponent();
            saveFileDialog = new SaveFileDialog();
            saveFileDialog.CheckFileExists = false;
            saveFileDialog.CheckPathExists = false;
            saveFileDialog.AddExtension = true;
            saveFileDialog.OverwritePrompt = false;
            saveFileDialog.DefaultExt = "txt";
            saveFileDialog.Filter = "文本|*.txt|日志|*.log|压缩|*.gz|所有|*.*";
            openFileDialog = new OpenFileDialog();
            openFileDialog.CheckFileExists = true;
            openFileDialog.Multiselect = false;
        }

        private void BtnDo_Click(object sender, RoutedEventArgs e)
        {
            if (string.IsNullOrWhiteSpace(tbSourceFile.Text) || string.IsNullOrWhiteSpace(tbTargetFile.Text) || string.IsNullOrWhiteSpace(tbSearch.Text))
            {
                System.Windows.MessageBox.Show("请输入文件完整的路径和查询条件");
                return;
            }
            try
            {
                String searchText = tbSearch.Text;
                if (searchText.Length < 4)
                {
                    System.Windows.MessageBox.Show("搜索内容最少4个字符");
                    return;
                }
                byte[] searchBytes = Encoding.UTF8.GetBytes(searchText);
                string sourceFile = tbSourceFile.Text;

                if (sourceFile == tbTargetFile.Text)
                {
                    System.Windows.MessageBox.Show("目标文件和源文件不能相同");
                    return;
                }

                StreamWriter sw = new StreamWriter(tbTargetFile.Text, false, Encoding.UTF8);
                rootLayout.IsEnabled = false;
                Thread thread = new Thread(new ThreadStart(() =>
                {
                    FileStream sourceFS = null;
                    try
                    {
                        long start = DateTime.Now.Ticks;
                        int search_count = 0;

                        if (System.IO.File.Exists(sourceFile))
                        {
                            sourceFS = new FileStream(sourceFile, FileMode.Open);
                            int bufSize = 101 * 1024 * 1024;
                            const int takeBufSize = 1024 * 1024;
                            byte[] buf = new byte[bufSize];
                            int bufEndPos = 0;
                            
                            SearchResult searchResult;

                            while (true)
                            {
                                int maxReadCount = bufSize - bufEndPos;
                                int readCount = sourceFS.Read(buf, bufEndPos, maxReadCount);
                                bufEndPos += readCount;

                                if (bufEndPos < takeBufSize)
                                {
                                    // 查找剩余内容
                                    if (bufEndPos >= searchBytes.Length)
                                    {
                                        searchResult = search(buf, bufEndPos, 0, bufEndPos, searchBytes, sw);
                                        search_count += searchResult.search_count;
                                        bufEndPos = searchResult.bufEndPos;
                                    }
                                    break;
                                }
                                
                                searchResult = search(buf, bufEndPos, 0, bufEndPos - takeBufSize, searchBytes, sw);
                                search_count += searchResult.search_count;
                                bufEndPos = searchResult.bufEndPos;

                                if (readCount < maxReadCount || 0 == readCount)
                                {
                                    // 查找剩余内容
                                    if (bufEndPos >= searchBytes.Length)
                                    {
                                        searchResult = search(buf, bufEndPos, 0, bufEndPos, searchBytes, sw);
                                        search_count += searchResult.search_count;
                                        bufEndPos = searchResult.bufEndPos;
                                    }
                                    break;
                                }
                            }

                            sourceFS.Close();
                            sourceFS = null;
                        }

                        sw.Flush();
                        sw.Close();
                        this.Dispatcher.BeginInvoke(new Action(() =>
                        {
                            rootLayout.IsEnabled = true;
                            long times = (DateTime.Now.Ticks - start) / 10000000;
                            tbMsg.Text = string.Format("处理完成，匹配到{0}行，耗时{1}", search_count, times);
                        }));
                    }
                    catch (Exception ex)
                    {
                        sw.Close();
                        if (null != sourceFS)
                        {
                            sourceFS.Close();
                        }
                        System.Windows.MessageBox.Show(ex.ToString());
                    }
                }));
                thread.Start();

            }
            catch (Exception ex)
            {
                System.Windows.MessageBox.Show(ex.Message);
            }
        }

        private SearchResult search(byte[] buf, int bufEndPos, int searchStartPos, int searchEndPos,
            byte[] searchBytes,
            StreamWriter sw)
        {
            int search_count = 0;
            while (searchStartPos < searchEndPos)
            {
                int searchIdx = searchFirstOneOfBytes(buf, searchStartPos, searchEndPos, searchBytes);

                if (searchIdx >= 0)
                {
                    string line = resolveLineFromPos(buf, bufEndPos, searchIdx, searchBytes.Length);
                    sw.WriteLine(line);
                    sw.Flush();
                    search_count++;

                    searchStartPos = searchIdx + searchBytes.Length;
                }
                else
                {
                    int moveStart = searchEndPos - (searchBytes.Length - 1);
                    if (moveStart < 0)
                    {
                        moveStart = 0;
                    }
                    memmove(buf, moveStart, bufEndPos, 0);
                    bufEndPos = bufEndPos - moveStart;
                    break;
                }
            }
            return new SearchResult(search_count, bufEndPos);
        }
        // 查找第一个匹配的位置
        private int searchFirstOneOfBytes(byte[] bytes, int bytesStartIndex, int bytesEndIndex, byte[] searchBytes)
        {
            byte searchFirstByte = searchBytes[0];
            int searchEndPos = bytesEndIndex - (searchBytes.Length-1);
            int i, j;
            for (i= bytesStartIndex; i< searchEndPos; ++i)
            {
                if (bytes[i] != searchFirstByte)
                {
                    continue;
                }
                
                for (j=1; j< searchBytes.Length; ++j)
                {
                    if (bytes[i+j] != searchBytes[j])
                    {
                        break;
                    }
                }
                if (j == searchBytes.Length)
                {
                    return i;
                }
            }
            return -1;
        }
        // 从byte数组的指定位置 查找 匹配位置所在行的内容
        private string resolveLineFromPos(byte[] buf, int bufEndPos, int matchIdx, int matchBytesLength)
        {
            int lineStart = 0, lineEnd = 0;

            // 向前搜索行首 最多搜索4K (防止一行过大)
            int startLineSearchMinIndex = System.Math.Max(0, matchIdx - 4 * 1024);
            int start;
            for (start = matchIdx; start >= startLineSearchMinIndex; --start)
            {
                if (buf[start] == '\n')
                {
                    lineStart = start + 1;
                    break;
                }
            }
            if (start < startLineSearchMinIndex)
            {
                lineStart = 0;
            }

            // 向后搜索行尾 最多搜索4K (防止一行过大)
            int endLineSearchMaxIndex = System.Math.Min(bufEndPos, matchIdx + matchBytesLength + 4 * 1024);
            int end;
            for (end = matchIdx + matchBytesLength; end < endLineSearchMaxIndex; ++end)
            {
                if (buf[end] == '\n')
                {
                    lineEnd = end + 1;
                    break;
                }
            }
            if (end >= endLineSearchMaxIndex)
            {
                lineEnd = bufEndPos;
            }
            
            return System.Text.Encoding.Default.GetString(buf, lineStart, lineEnd- lineStart);
        }
        // 数组的内存移动
        private void memmove(byte[] buf, int sourceStartPos, int sourceEndPos, int destStartPos)
        {
            int count = sourceEndPos - sourceStartPos;
            if (sourceStartPos < destStartPos) {
                // 从后往前复制
                for (int i= count-1; i >= 0; --i)
                {
                    buf[destStartPos+i] = buf[sourceStartPos+i];
                }
            } else {
                // 从前往后复制
                for (int i = 0; i < count; ++i)
                {
                    buf[destStartPos + i] = buf[sourceStartPos + i];
                }
            }
        }



        private void Btn_OpenFiles_Click(object sender, RoutedEventArgs e)
        {
            if (openFileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                tbSourceFile.Text = openFileDialog.FileName;
            }
        }

        private void Btn_SaveFile_Click(object sender, RoutedEventArgs e)
        {
            if (saveFileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                tbTargetFile.Text = saveFileDialog.FileName;
            }
        }

        private void BtnShowReadMe_Click(object sender, RoutedEventArgs e)
        {
            string tip = "只支持一个条件";
            System.Windows.MessageBox.Show(tip);
        }

        private void TBSearch_TextChanged(object sender, TextChangedEventArgs e)
        {
            if (string.IsNullOrEmpty(tbSearch.Text))
            {
                tbSearch.Opacity = 0.4;
            }
            else
            {
                tbSearch.Opacity = 1;
            }
        }
    }
}
